/**
 * @Created Nov 24, 2010 10:52:38 AM
 * @author cry30
 */
package com.philip.journal.core.service;

import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.modules.junit4.PowerMockRunner;

import com.philip.core.BusinessServiceProxy;
import com.philip.journal.core.Constant;
import com.philip.journal.core.JournalTestCommon;
import com.philip.journal.core.bean.User;
import com.philip.journal.core.dao.DAOFacade;
import com.philip.journal.core.dao.UserDAO;
import com.philip.journal.home.bean.Branch;
import com.philip.journal.home.bean.ConfigItem;
import com.philip.journal.home.dao.BranchDAO;
import com.philip.journal.home.dao.ConfigItemDAO;
import com.philip.journal.home.dao.EntryDAO;

/**
 * Base class for all Service Test class utilizing PowerMock.
 * 
 * Use {@link #getSession()} as the first parameter to the method call.
 * 
 * @param <T>
 */
@RunWith(PowerMockRunner.class)
public abstract class AbstractPowerMockJournalTestService<T extends BaseService> {

    /** @return Class of the instance to test. */
    protected abstract Class<T> getTestInstanceClass();

    /** Subclass logger. */
    private final Log logger = LogFactory.getLog(getClass());

    /** Test artifact. Test User. */
    private transient User testUser;

    /** Test artifact. Root Branch. */
    private transient Branch testRootBranch;

    /** The object instance to test. */
    private T testInstance;

    /** Mock DAO Facade instance. */
    private transient DAOFacade daoFacade = Mockito.mock(DAOFacade.class);

    /** Test Business Session Data. */
    private transient Map<String, Object> session;

    /** Refactored common functionality. */
    private final JournalTestCommon common = new JournalTestCommon();

    /** Initialize user, session and facade for test subclass reuse. */
    @Before
    public void setUp() {
        setSession(new HashMap<String, Object>());
        testUser = new User(JournalTestCommon.TEST_USERNAME, JournalTestCommon.TEST_PASSWORD,
                JournalTestCommon.TEST_CHECKSUM);
        getSession().put(Constant.CURRENT_USER, testUser);

        final ServiceFacade mockedFacade = Mockito.mock(ServiceFacade.class);
        getSession().put(BusinessServiceProxy.SERVICE_FACADE, mockedFacade);

        final BranchDAO mockBranchDAO = mock(BranchDAO.class);
        final EntryDAO mockEntryDAO = mock(EntryDAO.class);
        final UserDAO mockUserDAO = mock(UserDAO.class);
        final ConfigItemDAO mockConfigDAO = mock(ConfigItemDAO.class);

        when(daoFacade.getConfigItemDAO()).thenReturn(mockConfigDAO);
        when(mockConfigDAO.readAll()).thenReturn(new ArrayList<ConfigItem>());

        when(daoFacade.getBranchDAO()).thenReturn(mockBranchDAO);
        testRootBranch = new Branch(Constant.ROOT_NAME, null); //mock(Branch.class);
        testRootBranch.setBranchId(Constant.ROOT_ID);
        when(mockBranchDAO.read(Constant.ROOT_ID)).thenReturn(testRootBranch);
        /*
         * when(TEST_ROOT_BRANCH.getBranchId()).thenReturn(Constant.ROOT_ID);
         * when(TEST_ROOT_BRANCH.getName()).thenReturn(Constant.ROOT_NAME);
         */
        when(daoFacade.getEntryDAO()).thenReturn(mockEntryDAO);

        when(daoFacade.getUserDAO()).thenReturn(mockUserDAO);
        when(mockUserDAO.readByUsername(eq(JournalTestCommon.TEST_USERNAME))).thenReturn(testUser);

        try {
            testInstance = getTestInstanceClass().newInstance();
        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
        }
        testInstance.setDaoFacade(daoFacade);
    }

    /** Finalize DAO's for test subclass reuse. */
    @After
    public void tearDown() {
        //Need to reset container instantiated mocks.
        //        Mockito.reset(getDaoFacade().getUserDAO());
        //        Mockito.reset(getDaoFacade().getBranchDAO());
        //        Mockito.reset(getDaoFacade().getConfigItemDAO());
        //        Mockito.reset(getDaoFacade().getEntryDAO());
    }

    protected User getTestUser() {
        return (User) getSession().get(Constant.CURRENT_USER);
    }

    /**
     * @return the daoFacade
     */
    public DAOFacade getDaoFacade() {
        return daoFacade;
    }

    /** @return the session */
    public Map<String, Object> getSession() {
        return session;
    }

    /** @param pSession the session to set. */
    public void setSession(final Map<String, Object> pSession) {
        this.session = pSession;
    }

    public Log getLogger() {
        return logger;
    }

    protected JournalTestCommon getCommon() {
        return common;
    }

    protected T getTestInstance()
    {
        return testInstance;
    }

    protected T getSpyInstance()
    {
        return Mockito.spy(testInstance);
    }

    public Branch getTestRootBranch()
    {
        return testRootBranch;
    }

}
